package com.functest.orm

import com.functest.util.OrmUtil
import com.google.common.base.Preconditions

/**
 * @author: DavydovMD
 * Date: 10.04.13
 * Time: 11:08
 */
enum ConditionOp {
    EQ(' = '), NEQ(' <> '), GT(' > '), LT('<'), GTE(' >= '), LTE(' <= '),
    IN(' IN '), NOT_IN(' NOT IN '), IS_NULL(' IS NULL '), IS_NOT_NULL(' IS NOT NULL '),
    LIKE(' LIKE '), NOT_LIKE(' NOT LIKE '),
    BTEQ(' bitwise eq '), BTNEQ(' bitwise neq '), BTANY(' has any of bits')

    String sqlExpression


    ConditionOp(def sqlExpr) {
        sqlExpression = sqlExpr
    }

    GString buildSql(def value = null) {
        if (!([IS_NULL, IS_NOT_NULL].contains(this))) {
            Preconditions.checkArgument(value != null, "Value cannot be null for $this in buildSql", [])
        }

        if ([IN, NOT_IN].contains(this)) {
            Preconditions.checkArgument((value instanceof Collection), "Only collections are supported as operands for IN/NOT IN, but ${value?.class} encountered!", [])
            if (value) {
                return OrmUtil.asGString(SqlMappingUtil.buildInClause(this, value as List))
            }
            return this == IN ? OrmUtil.asGString(' IN (NULL /* IN(EMPTY_SET) */)  ') : OrmUtil.asGString(' + NULL IS NULL /* NOT_IN(EMPTY_SET) */  ')
        }

        Preconditions.checkArgument(!(value instanceof Collection), "Collection is not valid argument for ${this}.buildSql!", [])
        if ([IS_NULL, IS_NOT_NULL].contains(this)) {
            return OrmUtil.asGString(sqlExpression)
        } else if (this == BTEQ) {
            Preconditions.checkArgument(value instanceof Number || value instanceof FieldReference)
            return " & ${value} <> 0 "
        } else if (this == BTNEQ) {
            Preconditions.checkArgument(value instanceof Number || value instanceof FieldReference)
            return " & ${value} = 0 "
        }  else if (this == BTANY) {
            Preconditions.checkArgument(value instanceof Number || value instanceof FieldReference)
            return " | ${value} <> 0 "
        }


        if ([EQ, NEQ, GT, LT, GTE, LTE, LIKE, NOT_LIKE].contains(this)) {
            if (value instanceof FieldReference) {
                return OrmUtil.asGString(sqlExpression).plus(value.fqn)
            }
            if ([LIKE, NOT_LIKE].contains(this)) {
                Preconditions.checkArgument(value instanceof String || value instanceof GString)
            }
            return OrmUtil.asGString(sqlExpression).plus(" ${value} ")
        }
        throw new UnsupportedOperationException("Unsupported operation: $this")
    }



    boolean apply(def left, right, boolean sqlNullRule = false) {

        if ([BTEQ, BTNEQ, BTANY].contains(this)) {
            Preconditions.checkArgument(right != null)
            Preconditions.checkArgument(Number.isAssignableFrom(right?.class))
            if (left != null) Preconditions.checkArgument(Number.isAssignableFrom(left?.class))
        }

        if ([IN, NOT_IN].contains(this)) {
            Preconditions.checkArgument(right instanceof Collection, "For $this, right side must NOT be a collection, but it is ${right?.class}", [])
        } else if (sqlNullRule) {
            Preconditions.checkArgument(!(right instanceof Collection), "For $this, right side must be a collection, but it is ${right?.class}", [])
        }

        if ([LIKE, NOT_LIKE].contains(this)) {
            Preconditions.checkArgument(right instanceof String || right instanceof GString)
            Preconditions.checkArgument(left == null|| left instanceof String || left instanceof GString)
        }

        if ([GT, GTE, LT, LTE].contains(this)) {
            Preconditions.checkArgument(right != null)
        }

        if (sqlNullRule && !([IS_NOT_NULL, IS_NULL].contains(this))) {

            if (right == null)
                throw new IllegalArgumentException("Null is not supported for $this as right-side argument!")

            if (left == null) {
                return false
            }
        }

        switch (this) {
            case ConditionOp.EQ:
                return left == right
            case ConditionOp.NEQ:
                return left != right
            case ConditionOp.GT:
                if (left == null) return false
                return left > right
            case ConditionOp.LT:
                if (left == null) return true
                return left < right
            case ConditionOp.GTE:
                if (left == null) return false
                return left >= right
            case ConditionOp.LTE:
                if (left == null) return true
                return left <= right
            case ConditionOp.IN:
                return right?.contains(left)
            case ConditionOp.NOT_IN:
                return !right?.contains(left)
            case ConditionOp.IS_NULL:
                return left == null
            case ConditionOp.LIKE:
                if (left == null)
                    return false
                return OrmUtil.like(left as String, right as String)
            case ConditionOp.NOT_LIKE:
                if (left == null)
                    return true
                return !OrmUtil.like(left as String, right as String)
            case ConditionOp.IS_NOT_NULL:
                return left != null
            case ConditionOp.BTEQ:
                return (((left != null) ? left : 0) & (right)) == 1
            case ConditionOp.BTNEQ:
                return (((left != null) ? left : 0) & (right)) == 0
            case ConditionOp.BTANY:
                return (((left != null) ? left : 0) | (right)) == 1
        }
        throw new UnsupportedOperationException("Unsupported operation: ${left} ${this} ${right}")
    }

}
